package com.zkh.myutils.bean;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import com.zkh.myutils.annotation.Format;
import com.zkh.myutils.annotation.Ignore;
import com.zkh.myutils.utils.DateUtils;
import com.zkh.myutils.utils.StringUtils;
import com.zkh.myutils.utils.UtilsException;

/**
 * 实体类转换工具类，不依赖第三方jar包。
 * @author zkh
 *
 */
public class BeanUtils {

	//全局转换器
	static TypeConverter converter = new TypeConverter();
	
	/**
	 * 注册转换器，全局方法，一个地方设置，所有地方均会生效
	 * @param converter 转换器
	 */
	public static void setConverter(TypeConverter converter) {
		BeanUtils.converter = converter;
	}
	
	/**
	 * List&lt;Object&gt;转换List&lt;T&gt;，Object为基础类型包装类或Map
	 * @param list List集合
	 * @param bean 待转换的对象，可以是基础类型的包装类或实体对象
	 * @return
	 */
	public static <T> List<T> listToBean(List<Object> list, Class<T> bean){
		return listToBean(list, bean, null);
	}
	
	/**
	 * List&lt;Object&gt;转换List&lt;T&gt;，Object为基础类型包装类或Map
	 * @param list List集合
	 * @param bean 待转换的对象，可以是基础类型的包装类或实体对象
	 * @param converter 转换器
	 * @return
	 */
	@SuppressWarnings("unchecked")
	public static <T> List<T> listToBean(List<Object> list, Class<T> bean, TypeConverter converter){
		//容器
		List<T> ctn = new ArrayList<T>();
		//为空
		if(list==null || list.size()==0) {
			return ctn;
		}
		//目标类型
		String typeName = bean.getSimpleName(), typeComName = bean.getName();
		//转换类型
		int tranType = 0;
		//自定义解析器
		TypeConverterItem<?> coverter = converter!=null ? converter.getConverter(bean) : null;
		//全局解析器
		if(coverter==null) {
			coverter = BeanUtils.converter.getConverter(bean);
		}
		//自定义解析器
		if(coverter!=null) {
			tranType = 1;
		}
		//基础类型
		else if(BeanBaseTypeConverter.notParseType.contains(typeName)) {
			tranType = 2;
		}
		//日期类型
		else if("java.util.Date".equals(typeComName) || "java.sql.Timestamp".equals(typeComName)) {
			tranType = 3;
		}else if(bean.isArray()){
			tranType = 4;
		}else if(BeanArrayConverter.collectionType.contains(typeComName)) {
			tranType = 5;
		}else if("java.lang.String".equals(typeComName)){
			tranType = 7;
		}else {
			tranType = 6;
		}
		//遍历
		for(Object o: list) {
			//为空
			if(o==null) {
				ctn.add(null);continue;
			}
			//按类型执行
			switch (tranType) {
				//自定义类型
				case 1 :ctn.add((T) coverter.convertToProperty(o));break;
				//基础类型
				case 2 :{
					//类型名称
					String typename = "int".equals(typeName) ? "Integer" : ("char".equals(typeName) ? "Character" : StringUtils.uppercaseFirst(typeName));
					//转换类型并保存
					ctn.add((T) BeanBaseTypeConverter.toBaseType(typename, o));
				};break;
				//日期类型
				case 3 :{
					String t = o.toString();
					//按给定的pattern创建SimpleDateFormat对象
					SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
					try {
						//值
						ctn.add((T) sdf.parse(t));
					}catch(ParseException e) {
						sdf.applyPattern("yyyy-MM-dd");
						try {
							//值
							ctn.add((T) sdf.parse(t));
						}catch(ParseException e1) {
							throw new BeanTransException("日期类型数据格式化失败", e1);
						}
					}
				};break;
				//数组
				case 4 :ctn.add((T) BeanArrayConverter.toArray(o, o.getClass(), null, bean));break;
				//集合
				case 5 :ctn.add((T) BeanArrayConverter.toList(o, o.getClass(), null, bean, bean.getGenericSuperclass()));break;
				//字符串
				case 7 :ctn.add((T) (o==null ? null : o.toString()));break;
				//其它类型
				default :{
					if(o instanceof Map) {
						ctn.add(mapToBean((Map<String, ?>) o, bean));
					}else {
						throw new BeanTransException("类型"+o.getClass().getName()+"无法转换为类型"+typeName);
					}
				};break;
			}
		}
		//容器
		return ctn;
	}

	/**
	 * Map转实体对象
	 * @param map Map容器
	 * @param bean 待转换对象
	 * @return
	 */
	public static <T> T mapToBean(Map<String, ?> map, Class<T> bean) {
		return mapToBean(map, bean, null);
	}
	
	/**
	 * Map转实体对象
	 * @param map Map容器
	 * @param bean 待转换对象
	 * @param converter 临时转换器
	 * @return
	 */
	@SuppressWarnings({"rawtypes", "unchecked"})
	public static <T> T mapToBean(Map<String, ?> map, Class<T> bean, TypeConverter converter) {
		//实体类
		T en = null;
		//实例化实体类
		try {
			//目标对象是Map，则直接返回
			if(Map.class.getName().equals(bean.getName())) {
				return (T) map;
			}
			en = bean.newInstance();
		} catch (IllegalAccessException e) {
			throw new UtilsException("请提供可访问的无参构造器");
		} catch (InstantiationException e) {
			throw new UtilsException("实体类实例化失败");
		}
		//Class
		Class<?> cls = bean;
		//目标对象不为空且不是Object
		while(cls!=null && !"java.lang.Object".equals(cls.getName())) {
			//所有属性
			Field[] fields = cls.getDeclaredFields();
			//遍历
			for(Field field: fields) {
				//忽略
				if(field.isAnnotationPresent(Ignore.class)) {
					continue;
				}
				//字段名称
				String fldname = field.getName();
				//字段类型
				Class<?> fldType = field.getType();
				//字段类型名称（全称/简称）
				String typeName = fldType.getName(), simpleName = fldType.getSimpleName();
				//有值
				if(map.containsKey(fldname)) {
					//从Map中获取值
					Object value = map.get(fldname);
					//setter
					String setter = "set" + StringUtils.uppercaseFirst(fldname);
					//setter方法
					try {
						Method method = cls.getMethod(setter, fldType);
						//值为空
						if(value==null) {
							if(BeanBaseTypeConverter.baseType.contains(simpleName)){
								throw new BeanTransException("不能将null转为" + simpleName);
							}else {
								method.invoke(en, value);
							}
						}else {
							//获取转换器
							TypeConverterItem item = converter!=null ? converter.getConverter(fldType) : null;
							//全局转换器
							if(item==null) {
								item = BeanUtils.converter.getConverter(fldType);
							}
							//自定义转换器
							if(item!=null) {
								method.invoke(en, item.convertToProperty(value));
							}
							//字符串
							else if("java.lang.String".equals(typeName)) {
								method.invoke(en, value.toString());
							}
							//不进一步解析的类型
							else if(BeanBaseTypeConverter.notParseType.contains(fldType.getSimpleName())) {
								method.invoke(en, BeanBaseTypeConverter.toBaseType(simpleName, value));
							}
							//日期类型
							else if(StringUtils.isExist(typeName, "java.util.Date", "java.sql.Timestamp")) {
								//默认解析格式
								String pattern = "yyyy-MM-dd HH:mm:ss";
								//获取是否有注解
								Format fmt = field.getAnnotation(Format.class);
								//有注解
								if(fmt!=null) {
									pattern = fmt.pattern();
								}
								//值
								Date date = DateUtils.getDate(value.toString(), pattern);
								//保存值
								method.invoke(en, "java.util.Date".equals(typeName) ? date : new Timestamp(date.getTime()));
							}
							//数组
							else if(fldType.isArray()) {
								//获取并保存值
								method.invoke(en, BeanArrayConverter.toArray(value, value.getClass(), field, fldType));
							}
							//集合
							else if(BeanArrayConverter.collectionType.contains(typeName)) {
								//获取并保存值
								method.invoke(en, BeanArrayConverter.toList(value, value.getClass(), field, fldType, field.getGenericType()));
							}
							//其余视为Map转换Object
							else {
								//value值转Map
								if(value instanceof Map) {
									method.invoke(en, mapToBean((Map<String, ?>) value, fldType));
								}else {
									throw new BeanTransException("类型"+value.getClass().getName()+"无法转换为类型"+typeName);
								}
							}
						}
					} catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
						//无法访问，或没有setter，则直接忽略
					}
				}
			}
			//父类
			cls = cls.getSuperclass();
		}
		//返回实体
		return en;
	}
	
	/**
	 * 将Map&lt;String, String[]&gt;转为实体类，只针对web项目参数request.getParameterMap()进行解析。通用Map转实体请调用mapToBean
	 * @param map 键值为字符串的Map
	 * @param bean 要转换的实体类
	 */
	@SuppressWarnings("unchecked")
	public static <T> T parameterToBean(Map<String, String[]> map, Class<T> bean){
		//实体类
		T en = null;
		//实例化实体类
		try {
			//目标对象是Map，则直接返回
			if(Map.class.getName().equals(bean.getName())) {
				return (T) map;
			}
			en = bean.newInstance();
		} catch (IllegalAccessException e) {
			throw new UtilsException("请提供可访问的无参构造器");
		} catch (InstantiationException e) {
			throw new UtilsException("实体类实例化失败");
		}
		//map为空
		if(map==null || map.isEmpty()) {
			return en;
		}
		//Class
		Class<?> cls = bean;
		//目标对象不为空且不是Object
		while(cls!=null && !"java.lang.Object".equals(cls.getName())) {
			//获取字段
			Field[] fields = cls.getDeclaredFields();
			//遍历
			for(Field f: fields) {
				//忽略
				if(f.isAnnotationPresent(Ignore.class)) {
					continue;
				}
				//字段名
				String fieldName = f.getName();
				//有该字段值
				if(map.containsKey(fieldName)) {
					//赋值
					try {
						//类型Class
						Class<?> typeClass = f.getType();
						//字段类型
						String typeName = typeClass.getName(),simpleName = typeClass.getSimpleName();
						//Map中的值
						String[] val = map.get(fieldName);
						//setter
						String setter = "set" + StringUtils.uppercaseFirst(f.getName());
						//获取对应setter
						Method m = bean.getMethod(setter, typeClass);
						//值为空
						if(val==null) {
							//不是基础类型，赋值为null
							if(!BeanBaseTypeConverter.baseType.contains(simpleName)){
								m.invoke(en, (Object) null);
							}
						}else {
							//单值
							String singleValue = val.length==0 ? null : val[0];
							//基础类型
							if(BeanBaseTypeConverter.notParseType.contains(simpleName)) {
								m.invoke(en, BeanBaseTypeConverter.toBaseType(simpleName, singleValue));
							}
							//字符串
							else if("java.lang.String".equals(typeName)) {
								m.invoke(en, singleValue);
							}
							//日期类型
							else if(StringUtils.isExist(typeName, "java.util.Date", "java.sql.Timestamp")) {
								//默认解析格式
								String pattern = "yyyy-MM-dd HH:mm:ss";
								//获取是否有注解
								Format fmt = f.getAnnotation(Format.class);
								//有注解
								if(fmt!=null) {
									pattern = fmt.pattern();
								}
								//值
								Date date = DateUtils.getDate(singleValue, pattern);
								//保存值
								m.invoke(en, "java.util.Date".equals(typeName) ? date : new Timestamp(date.getTime()));
							}
							//数组
							else if(typeClass.isArray()) {
								//数组转换
								m.invoke(en, BeanArrayConverter.toArray(val, val.getClass(), f, typeClass));
							}
							//集合
							else if(BeanArrayConverter.collectionType.contains(typeName)) {
								m.invoke(en, BeanArrayConverter.toList(val, val.getClass(), f, typeClass, f.getGenericType()));
							}
						}
					} catch (SecurityException e) {
						throw new UtilsException(e);
					} catch (NoSuchMethodException | IllegalArgumentException | IllegalAccessException e) {
						//直接跳过
					}catch (InvocationTargetException e) {
						throw new UtilsException(e.getTargetException());
					}
				}
			}
			//父类
			cls = cls.getSuperclass();
		}
		return en;
	}
}
